﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Net;
using Microsoft.SharePoint.Publishing;
using System.Xml;
using Microsoft.SharePoint.Utilities;
using System.Diagnostics;

namespace Macaw.Moss2007.XmlSitemapSolution2
{
    public class XmlSitemapJobDefinition2 : SPJobDefinition
    {


        public XmlSitemapJobDefinition2()
            : base()
        {

        }

        public XmlSitemapJobDefinition2(SPWebApplication webapplication)
            : base(JobName, webapplication, null, SPJobLockType.Job)
        {
            this.Title = "XmlSitemapJobDefinition2";
        }

        public static string JobName = "XmlSitemapJob2";
        public static Guid JobId = new Guid("{CC08561B-4B81-4ce6-8A21-C815844105D6}");

        private const string SITEMAPNAMESPACE = "http://www.sitemaps.org/schemas/sitemap/0.9";

        protected void AddPublishingPageToSitemap(PublishingPage page, PublishingWeb web, XmlDocument sitemapdocument, XmlElement urlset, string XmlSitemapHostname)
        {
            try
            {
                XmlElement url = (XmlElement)urlset.AppendChild(sitemapdocument.CreateElement("url", SITEMAPNAMESPACE));

                XmlElement loc = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("loc", SITEMAPNAMESPACE));
                loc.InnerText = SPUrlUtility.CombineUrl(XmlSitemapHostname, page.ListItem.File.ServerRelativeUrl);

                XmlElement lastmod = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("lastmod", SITEMAPNAMESPACE));
                lastmod.InnerText = page.LastModifiedDate.ToString("yyyy-MM-dd");

                if (page.ListItem.Versions.Count > 1)
                {
                    SPListItemVersion liv_last = page.ListItem.Versions[page.ListItem.Versions.Count - 1];
                    SPListItemVersion liv_first = page.ListItem.Versions[0];
                    TimeSpan ts = liv_first.Created.Subtract(liv_last.Created);
                    double avghours = ts.TotalHours / page.ListItem.Versions.Count;

                    string changefreqvalue = null;

                    if (avghours >= 0 && avghours <= 24)
                    {
                        changefreqvalue = "daily";
                    }
                    else if (avghours > 24 & avghours <= 7 * 24)
                    {
                        changefreqvalue = "weekly";
                    }
                    else if (avghours > 7 * 24 && avghours <= 365 * 24)
                    {
                        changefreqvalue = "monthly";
                    }
                    else
                    {
                        changefreqvalue = "yearly";
                    }
                    if (!string.IsNullOrEmpty(changefreqvalue))
                    {
                        XmlElement changefreq = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("changefreq", SITEMAPNAMESPACE));
                        changefreq.InnerText = changefreqvalue;
                    }
                }

                XmlElement priority = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("priority", SITEMAPNAMESPACE));
                if (web.DefaultPage != null && web.DefaultPage.UniqueId.Equals(page.ListItem.UniqueId))
                {
                    priority.InnerText = "1.0";
                }
                else
                {
                    priority.InnerText = "0.5";
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(string.Format("{0}: Error at AddPublishingPageToSitemap \"{2}\":\r\n{1}", this.GetType().FullName, ex.ToString(), page.Url));
            }
        }

        protected void AddSitemapDocument(XmlDocument sitemapdocument, SPSite site, XmlDocument indexdocument, XmlElement sitemapindex, int count, string XmlSitemapHostname)
        {
            #region Save Sitemap file
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);
            xtw.Formatting = Formatting.Indented;
            xtw.WriteComment("Last updated on " + DateTime.Now.ToString());
            sitemapdocument.Save(xtw);            
            xtw.Flush();

            ms.Seek(0, System.IO.SeekOrigin.Begin);

            string sitemapname = string.Format("sitemap.{0}.xml", count);
            site.RootWeb.Files.Add(sitemapname, ms, true);
            ms.Close();
            Trace.WriteLine(string.Format("Updated \"{0}\"", sitemapname));
            #endregion

            #region Add saved Sitemap to SitemapIndex
            XmlElement index_sitemap = (XmlElement)sitemapindex.AppendChild(indexdocument.CreateElement("sitemap", SITEMAPNAMESPACE));
            XmlElement index_loc = (XmlElement)index_sitemap.AppendChild(indexdocument.CreateElement("loc", SITEMAPNAMESPACE));
            XmlElement index_lastmod = (XmlElement)index_sitemap.AppendChild(indexdocument.CreateElement("lastmod", SITEMAPNAMESPACE));

            index_loc.InnerText = SPUrlUtility.CombineUrl(XmlSitemapHostname, sitemapname);
            index_lastmod.InnerText = DateTime.Now.ToString("yyyy-MM-dd");
            #endregion
        }

        private const int MAXPAGESPERSITEMAP = 1000;

        private void ProcessSiteCollection(SPSite siteCollection)
        {
            string XmlSitemapHostname = siteCollection.RootWeb.Properties[ManageXmlSitemap.XMLSITEMAPURL];

            int sitemapdocumentcount = 0;
            XmlDocument indexdocument = new XmlDocument();
            XmlElement sitemapindex = (XmlElement)indexdocument.AppendChild(indexdocument.CreateElement("sitemapindex", SITEMAPNAMESPACE));

            #region Process Site Collection
            using (SPSite site = new SPSite(siteCollection.ID, siteCollection.SystemAccount.UserToken))
            {
                XmlDocument sitemapdocument = null;
                XmlElement urlset = null;
                foreach (SPWeb web in site.AllWebs)
                {
                    if (web.AllowAnonymousAccess)
                    {
                        if (PublishingWeb.IsPublishingWeb(web))
                        {
                            PublishingWeb pweb = PublishingWeb.GetPublishingWeb(web);
                            if (!pweb.PagesList.NoCrawl)
                            {
                                foreach (PublishingPage ppage in pweb.GetPublishingPages())
                                {
                                    if ((ppage.ListItem.ParentList.AnonymousPermMask64 & SPBasePermissions.ViewListItems) == SPBasePermissions.ViewListItems)
                                    {
                                        if (sitemapdocument == null)
                                        {
                                            sitemapdocument = new XmlDocument();
                                            urlset = (XmlElement)sitemapdocument.AppendChild(sitemapdocument.CreateElement("urlset", SITEMAPNAMESPACE));
                                        }
                                        AddPublishingPageToSitemap(ppage, pweb, sitemapdocument, urlset, XmlSitemapHostname);
                                        if (urlset.ChildNodes.Count >= MAXPAGESPERSITEMAP)
                                        {
                                            AddSitemapDocument(sitemapdocument, site, indexdocument, sitemapindex, sitemapdocumentcount, XmlSitemapHostname);
                                            sitemapdocumentcount++;
                                            sitemapdocument = null;
                                        }
                                    }

                                }
                            }
                        }
                    }
                }

                if (sitemapdocument != null)
                {
                    AddSitemapDocument(sitemapdocument, site, indexdocument, sitemapindex, sitemapdocumentcount, XmlSitemapHostname);
                    sitemapdocumentcount++;
                }
            }
            #endregion

            #region Save SitemapIndex
            System.IO.MemoryStream indexdocumentstream = new System.IO.MemoryStream();
            XmlTextWriter indexdocumentstreamwriter = new XmlTextWriter(indexdocumentstream, Encoding.UTF8);
            indexdocumentstreamwriter.WriteComment("Last updated on " + DateTime.Now.ToString());
            indexdocumentstreamwriter.Formatting = Formatting.Indented;
            indexdocument.Save(indexdocumentstreamwriter);            
            indexdocumentstreamwriter.Flush();

            indexdocumentstream.Seek(0, System.IO.SeekOrigin.Begin);
            System.IO.StreamReader indexdocumentstreamreader = new System.IO.StreamReader(indexdocumentstream);

            SPFile sitemapindexfile = siteCollection.RootWeb.Files.Add("sitemapindex.xml", indexdocumentstream, true);
            #endregion
        }

        public override void Execute(Guid targetInstanceId)
        {
            SPWebApplication webApplication = this.WebApplication;
            System.Diagnostics.Trace.WriteLine(string.Format("{0}: Execute start", this.GetType().FullName));
            try
            {

                foreach (SPSite siteCollection in this.WebApplication.Sites)
                {
                    try
                    {
                        if (siteCollection.RootWeb.Properties.ContainsKey(ManageXmlSitemap.XMLSITEMAPENABLED))
                        {
                            bool XmlSitemapEnabled = false;
                            if (bool.TryParse(siteCollection.RootWeb.Properties[ManageXmlSitemap.XMLSITEMAPENABLED], out XmlSitemapEnabled))
                            {
                                if (XmlSitemapEnabled)
                                {
                                    System.Diagnostics.Trace.WriteLine(string.Format("{0}: XmlSiteMap enabled for SiteCollection \"{1}\"", this.GetType().FullName, siteCollection.ServerRelativeUrl));
                                    ProcessSiteCollection(siteCollection);
                                }
                                else
                                {
                                    System.Diagnostics.Trace.WriteLine(string.Format("{0}: XmlSiteMap disabled for SiteCollection \"{1}\"", this.GetType().FullName, siteCollection.ServerRelativeUrl));
                                }
                            }
                            else
                            {
                                System.Diagnostics.Trace.WriteLine(string.Format("{0}: XmlSiteMap set invalid for SiteCollection \"{1}\"", this.GetType().FullName, siteCollection.ServerRelativeUrl));
                            }
                        }
                        else
                        {
                            System.Diagnostics.Trace.WriteLine(string.Format("{0}: XmlSiteMap not set for SiteCollection \"{1}\"", this.GetType().FullName, siteCollection.ServerRelativeUrl));
                        }
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("{0}: Error on SiteCollection \"{1}\":\r\n{2}", this.GetType().FullName, siteCollection.ServerRelativeUrl, ex.ToString()));
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(string.Format("{0}: Error:\r\n{1}", this.GetType().FullName, ex.ToString()));
            }
            System.Diagnostics.Trace.WriteLine(string.Format("{0}: Execute end", this.GetType().FullName));
            base.Execute(targetInstanceId);
        }
    }
}
